# -*- coding: utf-8 -*-
import json
import traceback
from xml.dom import minidom

from django.utils.encoding import smart_unicode

from common import orm
from common.admin import db as admin_user
from common.admin.model import ACTION, FORBIDDEN_ROLE, ACTION_TO_PERM
from common.admin.model import ROLE
from common.utils import JsonResponse, EnhencedEncoder
from common.utils import track_logging
from common.utils.api import get_client_ip
from common.utils.exceptions import Error
from common.utils.exceptions import PermissionError
from common.utils.respcode import StatusCodeDict
from common.utils.tz import local_now

_LOGGER = track_logging.getLogger(__name__)


def get_perm_url(url_array):
    """ 获取权限URL和资源ID """
    if not url_array.endswith('/'):
        url_array += '/'
    if url_array.split('/')[-2].isdigit():
        perm_url = '/'.join(url_array.split('/')[0:-2]) + '/'
        return perm_url, url_array.split('/')[-2]
    else:
        perm_url = '/'.join(url_array.split('/')[0:-1]) + '/'
        return perm_url, None
    return None, None


def get_perm(url_array, permission, role=0):
    permission = ACTION_TO_PERM[permission]
    perm_url, resource_id = get_perm_url(url_array)
    perm = admin_user.get_perm_id(perm_url, permission)
    if not role:
        return perm, resource_id
    if perm:
        role_data = admin_user.get_role(role)
        if unicode(perm.id) in role_data.permissions.split(','):
            return perm.as_dict(), resource_id
    return False, resource_id


def has_perm(target_perm, user_perm):
    if not target_perm:
        return True
    if not user_perm:
        return False
    user_perm = user_perm.split('|')
    target_perm = target_perm.split('|')
    intersects = list(set(user_perm) & set(target_perm))
    return False if not intersects else True


def check_perm(url_array, perm, role, user_perm):
    length = len(url_array)
    while length >= 1:
        url = '/'.join(url_array[0:length])
        if not url.endswith('/'):
            url += '/'
        k = admin_user.get_perm(url, perm)
        if k and k.min_role <= role and (role == ROLE.ADMIN or has_perm(k.user_perm, user_perm)):
            return True
        length -= 1
    return False


# 单独校验 上分接口权限
def check_user_make_order_success_permission(url, user_perm):
    if url.startswith('/pay/admin') and 'make_success' in url:
        if '95279527' in user_perm:
            return True
        else:
            return False
    else:
        return True


class UserMiddleware(object):
    """get user_id and token from header"""

    def process_request(self, req):
        user_id, token = req.META.get(
            'HTTP_X_AUTH_USER'), req.META.get('HTTP_X_AUTH_TOKEN')
        if not user_id:
            user_id, token = req.COOKIES.get(
                'pay_user_id'), req.COOKIES.get(
                'pay_user_token')
        if user_id and token:
            try:
                user_id = long(user_id)
            except ValueError:
                _LOGGER.error('user id format wrong!')
                req.user_id = None
                return
            if req.path.startswith('/pay/admin'):
                if not admin_user.check_token_vaild(user_id, token):
                    return JsonResponse(dict(
                        status=PermissionError.STATUS,
                        msg=str(u'permission not enough')),
                        status=PermissionError.HTTPCODE)
                info = admin_user.get_online_info(user_id, token)
                if info:
                    req.user_id = user_id
                    user = admin_user.get_user(user_id)
                    if user.role != FORBIDDEN_ROLE:
                        url_array = req.path
                        perm, _ = get_perm(url_array, req.method, user.role)
                        if not perm:
                            return JsonResponse(dict(
                                status=PermissionError.STATUS,
                                msg=str('permission not enough')),
                                status=PermissionError.HTTPCODE)
                        else:
                            req.user = user
                            return
                    else:
                        return JsonResponse(dict(
                            status=PermissionError.STATUS,
                            msg=str("user is forbidden or not activited")),
                            status=PermissionError.HTTPCODE)

        req.user_id = req.user = None

    def process_response(self, req, resp):
        if not req.path.startswith('/pay/admin'):
            return resp
        if req.method not in ACTION:
            return resp
        try:
            perm, resource_id = get_perm(req.path, req.method)
            content = json.loads(resp.content)
            status = content['status']
            if status != 0:
                return resp
            data = content['data']
            if resource_id is None:
                resource_id = data.get('order_id', None) if data.get('order_id', None) else data.get('id', None)
            if isinstance(data, dict):
                record = {
                    'resource': perm.desc,
                    'resource_id': resource_id,
                    'action': ACTION[req.method],
                    'content': req.body,
                    'operator': req.user_id
                }
                if req.path in (['/pay/admin/user/login/', '/pay/admin/user/']):
                    record['operator'] = data.get('id', 0)
                    record['content'] = 'login'
                admin_user.insert_record(record)
            elif isinstance(data, list):
                for d in data:
                    if 'id' not in d:
                        continue
                    else:
                        admin_user.insert_record({
                            'resource': perm.desc,
                            'resource_id': resource_id,
                            'action': ACTION[req.method],
                            'param': json.dumps(d, ensure_ascii=False,
                                                cls=EnhencedEncoder),
                            'operator': req.user_id
                        })
        except (ValueError, TypeError) as e:
            _LOGGER.exception(e)
            return resp

        return resp


class ApiGateMiddleware(object):
    """GateMiddleware

    Gate,大门,所有API访问的进出口.
    该middleware应该放在middlewares的第一位

    该middleware做了以下事情:
    为每个request编号;
    拦截所有request进入, 把trace_id和ip,data等信息打印到log;
    拦截所有request出去, 把trace_id和此次访问所耗时间打印到log;
    """

    def process_request(self, request):
        try:
            track_logging.clear_trace()
            request.incoming_time = local_now()
            request.ip = get_client_ip(request)

            # 生成request.DATA
            request.DATA = None
            if request.method == 'GET':
                request.DATA = request.GET
            else:
                body = request.body
                if body:
                    if request.META.get('CONTENT_TYPE', '').startswith('application/json'):
                        request.DATA = json.loads(smart_unicode(body))
                    elif 'xml' in request.META.get('CONTENT_TYPE', ''):
                        request.DATA = minidom.parseString(smart_unicode(body))
                    else:
                        request.DATA = smart_unicode(body)
        except:
            _LOGGER.exception('middleware process request fail')

    def process_view(self, request, view_func, view_args, view_kwargs):
        """打印request进入时的log"""
        try:
            user = request.user and '%s(%s)' % (request.user.id, request.user.nickname)
            _LOGGER.info("%s - %s, USER: %s, DATA: %s, IP: %s" % (
                request.method.upper(), request.path, user, request.DATA, request.ip))
        except:
            _LOGGER.exception('middleware process view fail')

    def process_response(self, request, response):
        """拦截所有request出去, 把trace_id和此次访问所耗时间打印到log"""
        try:
            orm.session.close()
            if request.method.upper() == 'OPTIONS':
                return response

            duration = int((local_now() - request.incoming_time).total_seconds() * 1000)
            _LOGGER.info("URL: %s: %s, Duration:%s" % (request.method.upper(), request.path, duration))
            # 对慢请求做出错报警
            if duration >= 6000:
                _LOGGER.error("slow request, url: %s, duration: %s" % (request.path, duration))
        except:
            _LOGGER.exception('middleware process response fail')

        return response

    def process_exception(self, request, e):
        try:
            if isinstance(e, Error):
                _LOGGER.info('server error! %s %s %s', e.HTTPCODE,
                             unicode(e) or StatusCodeDict.get(e.STATUS),
                             request.path)
                return JsonResponse(
                    dict(status=e.STATUS,
                         msg=unicode(e) or StatusCodeDict.get(e.STATUS)),
                    status=e.HTTPCODE)
            else:
                """拦截所有未处理的exception,并把traceback转化为一行数据打印到log"""
                user_repr = "%s (%s)" % (request.user.id, request.user.nickname) if request.user else ''

                _LOGGER.error(
                    'REQUEST URL: %s, USER: %s, Unexpected Exception: %s' % (
                        request.path, user_repr, traceback.format_exc()))
        except:
            _LOGGER.exception('middleware process exception fail')
